﻿using System.Collections.Generic;
using NUnit.Framework;

namespace Cpuss.Strategies.Tests
{

    /// <summary>
    /// Tests for Fcfs strategy.
    /// </summary>
    [TestFixture]
    public class FirstComeFirstServedTest
    {

        /// <summary>
        /// Check to see that the correct metrics of the simulation are collected during the simulation when the arrival times of the
        /// process are ordered.
        /// </summary>
        [Test]
        public void FcfsOrderedArrivalTimeMetricsTest()
        {
            ProcessLoad processes = new ProcessLoad()
            {
                new Process(1, 9, 0),
                new Process(2, 8, 10),
                new Process(3, 12, 16)
            };

            Dictionary<int, Process> expected = new Dictionary<int, Process>();
            expected.Add(1, new Process(1, 9, 0) { StartTime = 0, CompletionTime = 9 });
            expected.Add(2, new Process(2, 8, 10) { StartTime = 10, CompletionTime = 18 });
            expected.Add(3, new Process(3, 12, 16) { StartTime = 18, CompletionTime = 30 });

            Runner actual = new Runner(processes, new FirstComeFirstServed());

            actual.Run();

            for (int i = 1; i <= actual.Metrics.Count; i++)
            {
                Assert.AreEqual(expected[i].StartTime, actual.Metrics[i].StartTime);
                Assert.AreEqual(expected[i].CompletionTime, actual.Metrics[i].CompletionTime);
            }
        }

        /// <summary>
        /// Check to see that the correct metrics of the simulation are collected during the simulation when the arrival times of the
        /// process are unordered.
        /// </summary>
        [Test]
        public void FcfsUnorderedArrivalTimeMetricsTest()
        {
            ProcessLoad processes = new ProcessLoad()
            {
                new Process(2, 8, 10),
                new Process(1, 9, 0),
                new Process(3, 12, 16)
            };
            Runner actual = new Runner(processes, new FirstComeFirstServed());

            actual.Run();

            Dictionary<int, Process> expected = new Dictionary<int, Process>();
            expected.Add(1, new Process(1, 9, 0) { StartTime = 0, CompletionTime = 9 });
            expected.Add(2, new Process(2, 8, 10) { StartTime = 10, CompletionTime = 18 });
            expected.Add(3, new Process(3, 12, 16) { StartTime = 18, CompletionTime = 30 });

            for (int i = 1; i <= actual.Metrics.Count; i++)
            {
                Assert.AreEqual(expected[i].StartTime, actual.Metrics[i].StartTime);
                Assert.AreEqual(expected[i].CompletionTime, actual.Metrics[i].CompletionTime);
            }
        }

        /// <summary>
        /// Check to see that the correct metrics of the simulation can be extracted by hooking into the ProcessCompleted
        /// event of the Runner object.
        /// </summary>
        [Test]
        public void RealtimeSimulationMetricsProcessCompletedEventTest()
        {
            ProcessLoad processLoad = new ProcessLoad()
            {
                new Process(1, 9, 0),
                new Process(2, 2, 3),
                new Process(3, 26, 7),
                new Process(4, 42, 12),
                new Process(5, 3, 103)
            };
            ProcessLoad actual = new ProcessLoad();
            ProcessLoad expected = new ProcessLoad()
            {
                new Process(1, 9, 0) { StartTime = 0, CompletionTime = 9 },
                new Process(2, 2, 3) { StartTime = 9, CompletionTime = 11 },
                new Process(3, 26, 7) { StartTime = 11, CompletionTime = 37 },
                new Process(4, 42, 12) { StartTime = 37, CompletionTime = 79 },
                new Process(5, 3, 103) { StartTime = 103, CompletionTime = 106 }
            };

            Runner runner = new Runner(processLoad, new FirstComeFirstServed());
            runner.ProcessCompleted += (o, e) =>
                actual.Add(new Process(e.Id, e.BurstTime, e.ArrivalTime)
                {
                    StartTime = e.StartTime,
                    CompletionTime = e.CompletionTime
                });
            runner.Run();

            Assert.AreEqual(expected[0].StartTime, actual[0].StartTime);

            for (int i = 0; i < expected.Count; i++)
            {
                Assert.AreEqual(expected[i].StartTime, actual[i].StartTime);
                Assert.AreEqual(expected[i].CompletionTime, actual[i].CompletionTime);
            }
        }

        /// <summary>
        /// Check to see that the correct metrics of the simulation can be extracted by hooking into the ProcessStarted
        /// event of the Runner object.
        /// </summary>
        [Test]
        public void RealtimeSimulationMetricsProcessStartedEventTest()
        {
            ProcessLoad processLoad = new ProcessLoad()
            {
                new Process(2, 8, 10),
                new Process(1, 9, 0),
                new Process(3, 12, 16)
            };
            Runner runner = new Runner(processLoad, new FirstComeFirstServed());
            ProcessLoad actual = new ProcessLoad();
            ProcessLoad expected = new ProcessLoad()
            {
                new Process(1, 9, 0) { StartTime = 0 },
                new Process(2, 8, 10) { StartTime = 10 },
                new Process(3, 1, 16) { StartTime = 18 }
            };

            runner.ProcessStarted += (o, e) => actual.Add(new Process(e.Id, e.BurstTime, e.ArrivalTime) { StartTime = e.StartTime });

            runner.Run();

            for (int i = 0; i < expected.Count; i++)
            {
                Assert.AreEqual(expected[i].StartTime, actual[i].StartTime);
            }
        }

        /// <summary>
        /// Check to see that the correct string is returned.
        /// </summary>
        [Test]
        public void ToStringTest()
        {
            FirstComeFirstServed actual = new FirstComeFirstServed();

            Assert.AreEqual("First Come First Served", actual.ToString());
        }

    }

}
